package se.jocke.nbrs.service.impl;

import com.google.common.eventbus.Subscribe;
import com.google.common.io.CharStreams;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.fluent.Executor;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.fluent.Response;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.BasicCookieStore;
import org.openide.modules.OnStart;
import org.openide.util.Lookup;
import se.jocke.nbrs.model.RestService;
import se.jocke.nbrs.model.RestServiceResponse;
import se.jocke.nbrs.service.EventBus;
import se.jocke.nbrs.service.event.RestRequestFailed;
import se.jocke.nbrs.service.event.RestResponseEvent;
import se.jocke.nbrs.service.event.SendRequestEvent;

/**
 *
 * @author jocke
 *
 */
@OnStart
public class RestServiceSenderImpl implements Runnable {

    @Subscribe
    public void send(SendRequestEvent sendRequestEvent) {

        try {

            RestService restService = sendRequestEvent.getRestService();

            Executor executor = Executor.newInstance();

            BasicCookieStore basicCookieStore = new BasicCookieStore();

            executor.cookieStore(basicCookieStore);

            URL url;
            url = new URL(new URL(restService.getHosts().get(0)), restService.getPath());
            Request request = getByMethod(url, restService.getMethod());
            setHeaders(restService, request);

            if (restService.getUserName() != null && !restService.getUserName().isEmpty()) {
                Logger.getLogger(RestServiceSenderImpl.class.getName()).log(Level.INFO, "Setting auth");
                executor.auth(new AuthScope(AuthScope.ANY), new UsernamePasswordCredentials(restService.getUserName(), restService.getPassword()));
                executor.authPreemptive(new HttpHost(url.getHost(), url.getPort()));
            }

            request.setHeader("Accept", restService.getAccept());
            request.setHeader("Content-Type", restService.getContentType());

            if (restService.getBody() != null && !restService.getBody().isEmpty()) {
                request.bodyByteArray(restService.getBody().getBytes());
            }

            Response response = executor.execute(request);

            RestServiceResponse rsp = new RestServiceResponse();

            HttpResponse httpResp = response.returnResponse();

            rsp.setResponseLine(httpResp.getStatusLine().getReasonPhrase());
            rsp.setResponseCode(httpResp.getStatusLine().getStatusCode());

            if (httpResp.getStatusLine().getStatusCode() != 204) {
                rsp.setContentType(httpResp.getEntity().getContentType().getValue());
                rsp.setBody(CharStreams.toString(new InputStreamReader(httpResp.getEntity().getContent())));
            }

            rsp.setHeaders(getHeaders(httpResp.getAllHeaders()));
            rsp.setCookies(getCookieMap(basicCookieStore.getCookies()));

            Lookup.getDefault().lookup(EventBus.class).postAsync(new RestResponseEvent(rsp, restService));

        } catch (Exception ex) {
            Lookup.getDefault().lookup(EventBus.class).postAsync(new RestRequestFailed(ex, sendRequestEvent.getRestService()));
        }

    }

    private void setHeaders(RestService restService, Request request) {
        if (restService.getHeaders() != null) {
            Map<String, String> headers = restService.getHeaders();
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                request.setHeader(entry.getKey(), entry.getValue());
            }
        }
    }

    private Map<String, String> getHeaders(Header[] allHeaders) {

        Map<String, String> headers = new HashMap<String, String>();
        for (Header header : allHeaders) {
            headers.put(header.getName(), header.getValue());
        }

        return headers;
    }

    private Map<String, String> getCookieMap(List<Cookie> cookies) {
        Map<String, String> cookieMap = new HashMap<String, String>();

        for (Cookie cookie : cookies) {
            cookieMap.put(cookie.getName(), cookie.getValue());
        }

        return cookieMap;

    }

    private Request getByMethod(URL url, String method) throws URISyntaxException {

        if ("get".equalsIgnoreCase(method)) {
            return Request.Get(url.toURI());

        } else if ("post".equalsIgnoreCase(method)) {
            return Request.Post(url.toURI());

        } else if ("put".equalsIgnoreCase(method)) {
            return Request.Put(url.toURI());

        } else if ("head".equalsIgnoreCase(method)) {
            return Request.Head(url.toURI());

        } else if ("options".equalsIgnoreCase(method)) {
            return Request.Options(url.toURI());

        } else if ("trace".equalsIgnoreCase(method)) {
            return Request.Trace(url.toURI());
        }

        throw new UnsupportedOperationException("Http Method " + method + " is not known");
    }

    @Override
    public void run() {
        init();
    }

    public void init() {
        EventBus bus = Lookup.getDefault().lookup(EventBus.class);
        bus.register(this);
    }

}
